/*
 * Copyright 2010 - 2011, The PLDesktop Development Team
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */
#ifndef PLBUFFER_H
#define PLBUFFER_H

#include <kernel/PLObject.h>
#include <tools/PLArray.h>
#include <exceptions/PLIndexOutOfRangeException.h>
#include <exceptions/PLElementNotFoundException.h>
#include <exceptions/PLAppendEmptyElementException.h>
#include <exceptions/PLEmptyElementException.h>

namespace PLSDK {

    /**
     * Buffer for all parts of data.
     */
    template<typename bufferT>
    class PLBuffer : public virtual PLObject {
    public:
        /**
         * Constructor.
         */
        PLBuffer() : PLObject(), m_data(), m_count(0) {}
        /**
         * Constructor.
         *
         * @param size The size of the buffer.
         */
        PLBuffer(pluint size);
        /**
         * Constructor.
         *
         * @param b Element to add to the buffer.
         */
        PLBuffer(bufferT b);
        /**
         * Constructor.
         *
         * @param b Data to set into the buffer.
         * @param len Length of the data.
         * @exception PLElementNotFoundException The array b is a empty array.
         */
        PLBuffer(bufferT *b, pluint len);
        /**
         * Constructor.
         *
         * @param b Data to set into the buffer.
         * @param startPos The startposition in the buffer to copy.
         * @param len Length of the data.
         * @exception PLElementNotFoundException The array b is a empty array.
         */
        PLBuffer(bufferT *b, pluint startPos, pluint len);
        /**
         * Constructor.
         *
         * @param b Buffer to copy.
         * @exception PLElementNotFoundException The array b is a empty array.
         */
        PLBuffer(PLBuffer<bufferT> *b);
        /**
         * Constructor.
         *
         * @param b Buffer to copy.
         * @exception PLElementNotFoundException The array b is a empty array.
         */
        PLBuffer(const PLBuffer<bufferT> &b);
        /**
         * Constructor.
         *
         * @param b Buffer to copy.
         * @param start The start position to copy.
         * @param end The end position to copy.
         * @exception PLElementNotFoundException The array b is a empty array.
         */
        PLBuffer(PLArray<bufferT> &b, pluint start, pluint end);
        /**
         * Data from the buffer.
         *
         * @return Data from the buffer.
         */
        inline const bufferT *data() const;
        /**
         * The size of the buffer.
         *
         * @return The size of the buffer.
         */
        inline pluint capacity() const;
        /**
         * How many parts are in the buffer.
         *
         * @return The number of parts that are in the buffer.
         */
        inline pluint count() const;
        /**
         * Is the buffer a empty buffer.
         *
         * @return True if the buffer is empty.
         */
        inline plbool isEmpty() const;
        /**
         * Clear the buffer and delete all memory that was mapped for the buffer.
         */
        inline void clear();
        /**
         * Don't delete the mapped data but set the size to null. So we overright all
         * data.
         */
        inline void clean();
        /**
         * Set all data to null and than set the size to null. So no data was in the ram
         * and we don't remap data.
         */
        inline void secureClean();
        /**
         * Append one data at the end of the buffer.
         *
         * @param d Data to append.
         * @exception PLIndexOutOfRangeException This excpetion was thrown if the size of the new array
         * was to small and you whant to copy the data in the new array.
         * @exception PLElementNotFoundException If the old array have no data and you whant to copy the
         * data of the old array into the new array.
         */
        inline void append(bufferT d);
        /**
         * Append a array of data to the buffer.
         *
         * @param d Array of data.
         * @param len Length of the array of data.
         * @exception PLIndexOutOfRangeException This excpetion was thrown if the size of the new array
         * was to small and you whant to copy the data in the new array.
         * @exception PLElementNotFoundException If the old array have no data and you whant to copy the
         * data of the old array into the new array.
         * @exception PLAppendEmptyElementException The length of the data are empty.
         */
        inline void append(const bufferT *d, pluint len);
        /**
         * Append a array of data to the buffer.
         *
         * @param d Array of data.
         * @param startPos The startposition in the buffer to copy.
         * @param len Length of the array of data.
         * @exception PLIndexOutOfRangeException This excpetion was thrown if the size of the new array
         * was to small and you whant to copy the data in the new array.
         * @exception PLElementNotFoundException If the old array have no data and you whant to copy the
         * data of the old array into the new array.
         * @exception PLAppendEmptyElementException The length of the data are empty.
         */
        inline void append(const bufferT *d, pluint startPos, pluint len);
        /**
         * Append a array of data to the buffer.
         *
         * @param buffer Array of data.
         * @exception PLIndexOutOfRangeException This excpetion was thrown if the size of the new array
         * was to small and you whant to copy the data in the new array.
         * @exception PLElementNotFoundException If the old array have no data and you whant to copy the
         * data of the old array into the new array.
         * @exception PLAppendEmptyElementException The length of the data are empty.
         */
        inline void append(const PLArray<bufferT> &buffer);
        /**
         * Append a buffer of data to the buffer.
         *
         * @param d Array of data.
         * @exception PLIndexOutOfRangeException This excpetion was thrown if the size of the new array
         * was to small and you whant to copy the data in the new array.
         * @exception PLElementNotFoundException If the old array have no data and you whant to copy the
         * data of the old array into the new array.
         * @exception PLAppendEmptyElementException The length of the data are empty.
         */
        inline void append(PLBuffer<bufferT> *d);
        /**
         * Append a buffer of data to the buffer.
         *
         * @param d Array of data.
         * @exception PLIndexOutOfRangeException This excpetion was thrown if the size of the new array
         * was to small and you whant to copy the data in the new array.
         * @exception PLElementNotFoundException If the old array have no data and you whant to copy the
         * data of the old array into the new array.
         * @exception PLAppendEmptyElementException The length of the data are empty.
         */
        inline void append(const PLBuffer<bufferT> &d);
        /**
         * Cut data out of the buffer.<br>
         * <pre>
         * Values |1|3|4|
         * Pos    0 1 2 3
         * </pre>
         *
         * @param s Start point to cut.
         * @param e End point to cut.
         * @return The data that we cut out of the buffer.
         * @exception PLEmptyElementException The returnd value was a empty value.
         * @exception PLIndexOutOfRangeException If the index was not in the range of the array data.
         */
        inline bufferT *cutData(PLUInteger s, pluint e);
        /**
         * Ends the data of the buffer with the data of another buffer?
         *
         * @param b Buffer to test.
         * @return True if the buffer ends with the data of the buffer b.
         * @exception PLIndexOutOfRangeException If the buffer was smaller than the buffer to test.
         */
        inline plbool endsWith(const PLBuffer<bufferT> *b);
        /**
         * Ends the data of the buffer with the data of another buffer?
         *
         * @param b Buffer to test.
         * @return True if the buffer ends with the data of the buffer b.
         * @exception PLIndexOutOfRangeException If the buffer was smaller than the buffer to test.
         */
        inline plbool endsWith(const PLBuffer<bufferT> &b);
        /**
         * Ends the data of the buffer with the data?
         *
         * @param b Data to test.
         * @return True if the buffer ends with the data of b.
         * @exception PLIndexOutOfRangeException If the buffer was smaller than the data to test.
         */
        inline plbool endsWith(bufferT b);
        /**
         * Get the data of a part from the buffer.<br>
         * <pre>
         * Values |1|3|4|
         * Pos    0 1 2 3
         * </pre>
         *
         * @param s Startpoint of the part.
         * @param e Endpoint of the part.
         * @return The part of the buffer.
         * @exception PLEmptyElementException The returnd value was a empty value.
         * @exception PLIndexOutOfRangeException If the index was not in the range of the array data.
         */
        inline bufferT *data(pluint s, pluint e);
        /**
         * Return a buffer with a part of this buffer.<br>
         * <pre>
         * Values |1|3|4|
         * Pos    0 1 2 3
         * </pre>
         *
         * @param s Start point in the buffer.
         * @param e End point in the buffer.
         * @return The buffer with the part of the buffer.
         * @exception PLEmptyElementException The returnd value was a empty value.
         * @exception PLIndexOutOfRangeException If the index was not in the range of the array data.
         */
        inline PLBuffer<bufferT> subPart(pluint s, pluint e);
        /**
         * Return the data at the point p.
         *
         * @param p Point for that you want the data.
         * @return The data at the position p.
         * @exception PLIndexOutOfRangeException If the index was not in the range of the array data.
         */
        inline bufferT dataAt(pluint p) const;
        /**
         * Return the first index of a component.
         *
         * @param c The component to search for.
         * @param s Start position to search for.
         * @return The first index of the component c.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline pluint indexOf(bufferT c, pluint s = 0);
        /**
         * Return the first index of a buffer.
         *
         * @param c The buffer to search for.
         * @param s Start position to search for.
         * @return The first index of the component c.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline pluint indexOf(PLBuffer<bufferT> c, pluint s = 0);
        /**
         * Insert a component at the position p.
         *
         * @param c The component to add.
         * @param p Position where to add the component.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline void insertAt(bufferT c, pluint p);
        /**
         * Insert a component array at the position p.
         *
         * @param c The component array to add.
         * @param len The length of the component array.
         * @param p Position where to add the component.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline void insertAt(const bufferT *c, pluint len, pluint p);
        /**
         * Insert a component array at the position p.
         *
         * @param c The component array to add.
         * @param startPos The startposition in the buffer to copy.
         * @param len The length of the component array.
         * @param p Position where to add the component.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline void insertAt(const bufferT *c, pluint startPos, pluint len, pluint p);
        /**
         * Insert a component array at the position p.
         *
         * @param c The component array to add.
         * @param p Position where to add the component.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline void insertAt(const PLBuffer<bufferT> &c, pluint p);
        /**
         * Insert a component array at the position p.
         *
         * @param c The component array to add.
         * @param p Position where to add the component.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline void insertAt(const PLArray<bufferT> &c, pluint p);
        /**
         * Find the last position of c.
         *
         * @param c Component to search for.
         * @param s Start position to start for.
         * @return The last position of c.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline pluint lastIndexOf(bufferT c, pluint s);
        /**
         * Find the last position of c.
         *
         * @param c Component to search for.
         * @return The last position of c.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline pluint lastIndexOf(bufferT c);
        /**
         * Find the last position of c.
         *
         * @param c Component to search for.
         * @param s Start position to start for.
         * @return The last position of c.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline pluint lastIndexOf(PLBuffer<bufferT> c, pluint s);
        /**
         * Find the last position of c.
         *
         * @param c Component to search for.
         * @return The last position of c.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline pluint lastIndexOf(PLBuffer<bufferT> c);
        /**
         * Insert a component at the beginning of the buffer.
         *
         * @param c Component to add.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline void prepend(bufferT c);
        /**
         * Insert a component at the beginning of the buffer.
         *
         * @param c Component to add.
         * @param len The length of the component.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline void prepend(const bufferT *c, pluint len);
        /**
         * Insert a component at the beginning of the buffer.
         *
         * @param c Component to add.
         * @param startPos The startposition in the buffer to copy.
         * @param len The length of the component.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline void prepend(const bufferT *c, pluint startPos, pluint len);
        /**
         * Prepend a buffer of data to the buffer.
         *
         * @param d Array of data.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline void prepend(PLBuffer<bufferT> *d);
        /**
         * Prepend a buffer of data to the buffer.
         *
         * @param d Array of data.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline void prepend(PLBuffer<bufferT> d);
        /**
         * Prepend a buffer of data to the buffer.
         *
         * @param d Array of data.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline void prepend(PLArray<bufferT> *d);
        /**
         * Prepend a buffer of data to the buffer.
         *
         * @param d Array of data.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline void prepend(PLArray<bufferT> d);
        /**
         * Remove a part of the buffer.
         *
         * @param s Startpoint for the delete part.
         * @param e Endpoint for the delete part.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline void remove(pluint s, pluint e);
        /**
         * Array of components.
         *
         * @return The array of the components.
         */
        inline bufferT *array() const;
        /**
         * Array of components.
         *
         * @return The array of the components.
         */
        inline PLArray<bufferT> toArray() const;
        /**
         * Write a component to the buffer.<br>
         * It overright the existing component.
         *
         * @param p Position where to add the component.
         * @param c Component that you want to set.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline void write(pluint p, bufferT c);
        /**
         * Write a component array to the buffer.<br>
         * It overright the existing component.
         *
         * @param p Position where to add the component.
         * @param c Component array that you want to set.
         * @param len Length of the component array.
         * @exception PLIndexOutOfRangeException The start position was out of range.
         * @exception PLElementNotFoundException The element was not found in the buffer.
         */
        inline void write(pluint p, bufferT *c, pluint len);
        /**
         * Read the component from the position p.
         *
         * @param p Position of the component you want to read.
         * @return The component at the position p.
         */
        inline bufferT read(pluint p);
        /**
         * Read the component from the position p.
         *
         * @param p Position of the component you want to read.
         * @param len The length of the component you want to read.
         * @return The component at the position p.
         */
        inline bufferT *read(pluint p, pluint len);
        /**
         * Compare the buffer with another buffer data.
         *
         * @param data Buffer to compare to.
         * @return The compare result.
         */
        inline PLCompareResult compareTo(const PLBuffer<bufferT> &data) const;
        /**
         * Contains the buffer a value.
         *
         * @param value The value to test for a match for.
         * @return True if the buffer contains the value.
         */
        inline plbool contains(bufferT value);
        /**
         * Contains the buffer another buffer data.
         *
         * @param data The data to test for a match for.
         * @param length The length of the data.
         * @return True if the buffer contains the buffer.
         */
        inline plbool contains(const bufferT *data, pluint length);
        /**
         * Contains the buffer another buffer data.
         *
         * @param data The data to test for a match for.
         * @return True if the buffer contains the buffer.
         */
        inline plbool contains(const PLBuffer<bufferT> &data);
        /**
         * Contains the buffer another buffer data.
         *
         * @param data The data to test for a match for.
         * @return True if the buffer contains the buffer.
         */
        inline plbool contains(const PLArray<bufferT> &data);
        /**
         * Contains the buffer one of the unicode character in the array.
         *
         * @param unicodeData The data to test for.
         * @return True if the buffer contains one of the unicode character.
         */
        inline plbool containsOneOf(const PLArray<bufferT> &unicodeData);
        /**
         * Replace the part replace with the part with in this buffer.
         *
         * @param replace Part to search for.
         * @param with Part to replace with.
         */
        inline void replaceAll(PLBuffer<bufferT> *replace, PLBuffer<bufferT> *with);
        /**
         * Operator for the direct access to the data.
         */
        inline bufferT &operator[](pluint pos);
        /**
         * Operator for the direct access to the data.
         */
        inline const bufferT &operator[](pluint pos) const;
        /**
         * Compare the buffer.
         */
        inline bool operator == (PLBuffer<bufferT> data);
        /**
         * Compare the buffer.
         */
        inline bool operator != (PLBuffer<bufferT> data);
        /**
         * Is the buffer smaler than another buffer.
         */
        inline bool operator < (PLBuffer<bufferT> data);
        /**
         * Is the buffer lager than another buffer.
         */
        inline bool operator > (PLBuffer<bufferT> data);
        /**
         * Copy the buffer.
         */
        inline PLBuffer<bufferT> &operator =(const PLBuffer<bufferT> &buffer);
    protected:
        /**
         * Resize the buffer to the given size.
         *
         * @param c The size to resize to.
         * @exception PLIndexOutOfRangeException This excpetion was thrown if the size of the new array
         * was to small and you whant to copy the data in the new array.
         * @exception PLElementNotFoundException If the old array have no data and you whant to copy the
         * data of the old array into the new array.
         */
        void resize(pluint c) {
            if(m_data.isEmpty()) {
                m_data.resetSizeOfArray(c, false);
            } else {
                if(m_count + c > m_data.size()) {
                    if(m_count != 0) {
                        m_data.resetSizeOfArray(m_count + c, true);
                    } else {
                        m_data.resetSizeOfArray(m_count + c, false);
                    }
                }
            }
        }
        /**
         * The row data array.
         */
        PLArray<bufferT> m_data;
        /**
         * The count of the buffer.
         */
        pluint m_count;
    };

    template<typename bufferT>
    PLBuffer<bufferT>::PLBuffer(pluint size) : PLObject(), m_data(size > 0 ? size : 0), m_count(0) {
    }

    template<typename bufferT>
    PLBuffer<bufferT>::PLBuffer(bufferT b) : PLObject(), m_data(1), m_count(1) {
        m_data[0] = b;
    }

    template<typename bufferT>
    PLBuffer<bufferT>::PLBuffer(bufferT *b, pluint len) : PLObject(), m_data(len), m_count(len) {
        if(len > 0) {
            pluint i = 0;
            while(i < len) {
                m_data[i] = b[i];
                i++;
            }
        } else {
            plthrow(PLElementNotFoundException);
        }
    }

    template<typename bufferT>
    PLBuffer<bufferT>::PLBuffer(bufferT *b, pluint startPos, pluint len) : PLObject(), m_data(len - startPos), m_count(len - startPos) {
        if(len - startPos > 0) {
            pluint i = startPos;
            while(i < len) {
                m_data[i - startPos] = b[i];
                i++;
            }
        } else {
            plthrow(PLElementNotFoundException);
        }
    }

    template<typename bufferT>
    PLBuffer<bufferT>::PLBuffer(PLBuffer<bufferT> *b) : PLObject(), m_data(b->m_count), m_count(b->m_count) {
        if(b->m_count > 0) {
            pluint i = 0;
            while(i <= b->m_count) {
                m_data[i] = b->m_data[i];
                i++;
            }
        } else {
            plthrow(PLElementNotFoundException);
        }
    }

    template<typename bufferT>
    PLBuffer<bufferT>::PLBuffer(const PLBuffer<bufferT> &b) : PLObject(), m_data(b.m_count), m_count(b.m_count) {
        if(b.m_count > 0) {
            if(!b.isEmpty()) {
                for(pluint i = 0 ; i < b.m_count ; i++) {
                    m_data[i] = b.m_data.get(i);
                }
            }
        } else {
            plthrow(PLElementNotFoundException);
        }
    }

    template<typename bufferT>
    PLBuffer<bufferT>::PLBuffer(PLArray<bufferT> &b, pluint start, pluint end) : PLObject(), m_data(end - start), m_count(end - start) {
        if(b.size() > 0) {
            for(pluint i = start ; i < end ; i++) {
                m_data[i - start] = b[i];
            }
        } else {
            plthrow(PLElementNotFoundException);
        }
    }

    template<typename bufferT>
    const bufferT *PLBuffer<bufferT>::data() const {
        return m_data.data();
    }

    template<typename bufferT>
    pluint PLBuffer<bufferT>::capacity() const {
        return m_data.size();
    }

    template<typename bufferT>
    pluint PLBuffer<bufferT>::count() const {
        return m_count;
    }

    template<typename bufferT>
    plbool PLBuffer<bufferT>::isEmpty() const {
        return m_count == 0;
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::clear() {
        m_data.clear();
        m_count = 0;
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::clean() {
        m_count = 0;
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::secureClean() {
        for(pluint i = 0 ; i < m_data.size() ; i++) {
            m_data[i] = bufferT();
        }
        m_count = 0;
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::append(bufferT d) {
        resize(1);
        m_data[m_count] = d;
        m_count++;
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::append(const bufferT *d, pluint len) {
        if(len <= 0) {
            plthrow(PLAppendEmptyElementException);
        }
        resize(len);
        for(pluint i = 0 ; i < len ; i++) {
            m_data[m_count + i] = d[i.value()];
        }
        m_count += len;
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::append(const bufferT *d, pluint startPos, pluint len) {
        if(len - startPos <= 0) {
            plthrow(PLAppendEmptyElementException);
        }
        resize(len - startPos);
        for(pluint i = startPos ; i < len ; i++) {
            m_data[m_count + i - startPos] = d[i];
        }
        m_count += len - startPos;
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::append(const PLArray<bufferT> &buffer) {
        if(buffer.isEmpty()) {
            plthrow(PLAppendEmptyElementException);
        }
        resize(buffer.size());
        for(pluint i = 0 ; i < buffer.size() ; i++) {
            m_data[m_count + i] = buffer.get(i);
        }
        m_count += buffer.size();
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::append(PLBuffer<bufferT> *d) {
        if(d->count() <= 0) {
            plthrow(PLAppendEmptyElementException);
        }
        append(d->m_data);
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::append(const PLBuffer<bufferT> &d) {
        if(d.m_count <= 0) {
            plthrow(PLAppendEmptyElementException);
        }
        append(d.m_data);
    }

    template<typename bufferT>
    bufferT *PLBuffer<bufferT>::cutData(pluint s, pluint e) {
        if(e - s == 0) {
            plthrow(PLEmptyElementException);
        }
        if(e >= m_count || e > s) {
            plthrow(PLIndexOutOfRangeException);
        }
        bufferT *ret = data(s, e);
        remove(s, e);
        return ret;
    }

    template<typename bufferT>
    plbool PLBuffer<bufferT>::endsWith(const PLBuffer<bufferT> *b) {
        pluint endTest = m_count - 1 - b->m_count;
        if(endTest < 0) {
            plthrow(PLIndexOutOfRangeException);
        }
        for(pluint i = m_count - 1, j = b->m_count - 1 ; i > endTest ; i--, j--) {
            if(m_data[i] != b->m_data.get(j)) {
                return false;
            }
        }
        return true;
    }

    template<typename bufferT>
    plbool PLBuffer<bufferT>::endsWith(const PLBuffer<bufferT> &b) {
        return endsWith(&b);
    }

    template<typename bufferT>
    plbool PLBuffer<bufferT>::endsWith(bufferT b) {
        if(m_count == 0) {
            plthrow(PLIndexOutOfRangeException);
        }
        if(m_data[m_count - 1] == b) {
            return true;
        }
        return false;
    }

    template<typename bufferT>
    bufferT *PLBuffer<bufferT>::data(pluint s, pluint e) {
        if(e - s == 0) {
            plthrow(PLEmptyElementException);
        }
        if(e > m_count || e < s) {
            plthrow(PLIndexOutOfRangeException);
        }
        bufferT *ret = new bufferT[e - s];
        for(pluint i = s ; i < e ; i++) {
            ret[i - s] = m_data[i];
        }
        return ret;
    }

    template<typename bufferT>
    PLBuffer<bufferT> PLBuffer<bufferT>::subPart(pluint s, pluint e) {
        return PLBuffer<bufferT>(m_data, s, e);
    }

    template<typename bufferT>
    bufferT PLBuffer<bufferT>::dataAt(pluint p) const {
        if(p < m_count) {
            return m_data.get(p);
        }
        plthrow(PLIndexOutOfRangeException);
    }

    template<typename bufferT>
    pluint PLBuffer<bufferT>::indexOf(bufferT c, pluint s) {
        if(s >= m_count) {
            plthrow(PLIndexOutOfRangeException);
        }
        for(pluint i = s ; i < m_count ; i++) {
            if(m_data[i] == c) {
                return i;
            }
        }
        plthrow(PLElementNotFoundException);
    }

    template<typename bufferT>
    pluint PLBuffer<bufferT>::indexOf(PLBuffer<bufferT> c, pluint s) {
        if(s + c.m_count > m_count) {
            plthrow(PLIndexOutOfRangeException);
        }
        pluint j;
        for(pluint i = s ; i < m_count - c.count() ; i++) {
            if(m_data[i] == c.m_data[0]) {
                for(j = 1 ; j < c.m_count ; j++) {
                    if(m_data[i + j] != c.m_data[j]) {
                        break;
                    } else if(j == c.m_count - 1 || m_data[i + j] != c.m_data[j]) {
                        return i;
                    }
                }
            }
        }
        plthrow(PLElementNotFoundException);
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::insertAt(bufferT c, pluint p) {
        resize(1);
        pluint i;
        if(m_count > 0) {
            for(i = m_count - 1 ; i >= p ; --i) {
                m_data[i + 1] = m_data[i];
                if(p == 0 && i == 0) {
                    break;
                }
            }
        }
        m_data[p] = c;
        m_count++;
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::insertAt(const bufferT *c, pluint len, pluint p) {
        resize(len);
        pluint i;
        for(i = m_count - 1; i >= p ; --i) {
            m_data[i + len] = m_data[i];
            if(p == 0 && i == 0) {
                break;
            }
        }
        for(i = p ; i < p + len ; i++) {
            m_data[i] = c[i - p];
        }
        m_count += len;
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::insertAt(const bufferT *c, pluint startPos, pluint len, pluint p) {
        resize(len - startPos);
        pluint i;
        for(i = m_count - 1; i >= p ; --i) {
            m_data[i + len] = m_data[i];
            if(p == 0 && i == 0) {
                break;
            }
        }
        for(i = p + startPos ; i < p + len ; i++) {
            m_data[i - startPos] = c[i - p];
        }
        m_count += len;
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::insertAt(const PLBuffer<bufferT> &c, pluint p) {
        insertAt(c.data(), c.count(), p);
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::insertAt(const PLArray<bufferT> &c, pluint p) {
        insertAt(c.data(), c.size(), p);
    }

    template<typename bufferT>
    pluint PLBuffer<bufferT>::lastIndexOf(bufferT c, pluint s) {
        if(s >= m_count) {
            plthrow(PLIndexOutOfRangeException);
        }
        for(pluint i = s ; i >= 0 ; i--) {
            if(m_data[i] == c) {
                return i;
            }
            if(i == 0) {
                plthrow(PLElementNotFoundException);
            }
        }
        plthrow(PLElementNotFoundException);
    }

    template<typename bufferT>
    pluint PLBuffer<bufferT>::lastIndexOf(bufferT c) {
        return lastIndexOf(c, m_count - 1);
    }

    template<typename bufferT>
    pluint PLBuffer<bufferT>::lastIndexOf(PLBuffer<bufferT> c, pluint s) {
        if(s < 0 || s + c.count() >= m_count) {
            plthrow(PLIndexOutOfRangeException);
        }
        pluint j;
        for(pluint i = s ; i > c.count() ; i--) {
            for(j = 0 ; j > c.count() ; j++) {
                if(m_data[i + j] != c.m_data[j]) {
                    break;
                }
                if(j == 0) {
                    return i;
                }
            }
        }
        plthrow(PLElementNotFoundException);
    }

    template<typename bufferT>
    pluint PLBuffer<bufferT>::lastIndexOf(PLBuffer<bufferT> c) {
        if(m_count <= c.count()) {
            plthrow(PLIndexOutOfRangeException);
        }
        return lastIndexOf(c, m_count - 1 - c.count());
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::prepend(bufferT c) {
        insertAt(c, 0);
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::prepend(const bufferT *c, pluint len) {
        insertAt(c, len, 0);
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::prepend(const bufferT *c, pluint startPos, pluint len) {
        insertAt(c, startPos, len, 0);
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::prepend(PLBuffer<bufferT> *d) {
        prepend(d->m_data.data(), d->m_count);
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::prepend(PLBuffer<bufferT> d) {
        prepend(d.m_data.data(), d.m_count);
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::prepend(PLArray<bufferT> *d) {
        prepend(d->data(), d->m_count);
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::prepend(PLArray<bufferT> d) {
        prepend(d.data(), d.size());
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::remove(pluint s, pluint e) {
        pluint diff = e - s;
        if(diff == 0) {
            plthrow(PLElementNotFoundException);
        }
        if(diff < 0 || e > m_count) {
            plthrow(PLIndexOutOfRangeException);
        }
        for(pluint i = s ; i < m_count - e ; i++) {
            m_data[i - s] = m_data[i + e];
        }
        m_count -= diff;
    }

    template<typename bufferT>
    bufferT *PLBuffer<bufferT>::array() const {
        bufferT *b = new bufferT[m_count.value()];
        for(pluint i = 0 ; i < m_count ; i++) {
            b[i] = m_data[i];
        }
        return b;
    }

    template<typename bufferT>
    PLArray<bufferT> PLBuffer<bufferT>::toArray() const {
        PLArray<bufferT> ret(m_count);
        for(pluint i = 0 ; i < m_count ; i++) {
            ret[i] = m_data[i];
        }
        return ret;
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::write(pluint p, bufferT c) {
        if(p >= m_count) {
            resize(p - m_count);
        }
        m_data[p] = c;
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::write(pluint p, bufferT *c, pluint len) {
        if(p + len >= m_count) {
            resize(p + len - m_count);
        }
        for(pluint i = 0 ; i < len ; i++) {
            m_data[p + i] = c[i];
        }
    }

    template<typename bufferT>
    bufferT PLBuffer<bufferT>::read(pluint p) {
        return dataAt(p);
    }

    template<typename bufferT>
    bufferT *PLBuffer<bufferT>::read(pluint p, pluint len) {
        return data(p, len);
    }

    template<typename bufferT>
    PLCompareResult PLBuffer<bufferT>::compareTo(const PLBuffer<bufferT> &data) const {
        if(m_count == data.m_count) {
            for(pluint i = 0 ; i < m_count ; i++) {
                if(m_data[i] < data[i]) {
                    return PLCompareResultSmaller;
                } else if(m_data[i] > data[i]) {
                    return PLCompareResultBigger;
                }
            }
            return PLCompareResultSame;
        }
        return PLCompareResultSmaller;
    }

    template<typename bufferT>
    void PLBuffer<bufferT>::replaceAll(PLBuffer<bufferT> *replace, PLBuffer<bufferT> *with) {
        pluint j;
        for(pluint i = 0 ; i < m_count ; i++) {
            if(m_data[i] == replace->m_data[0]) {
                for(j = 0 ; j < replace->m_count ; j++) {
                    if(i + j > m_count) {
                        return;
                    }
                    if(j == replace->m_count - 1 && m_data[i + j] != replace->m_data[j]) {
                        remove(i, i + replace->m_count - 1);
                        insertAt(with->m_data.data(), with->m_count - 1, i);
                    } else if(m_data[i + j] != replace->m_data[j]) {
                        break;
                    }
                }
            }
        }
    }

    template<typename bufferT>
    plbool PLBuffer<bufferT>::contains(bufferT value) {
        for(pluint i = 0 ; i < m_count ; i++) {
            if(m_data[i] == value) {
                return true;
            }
        }
        return false;
    }

    template<typename bufferT>
    plbool PLBuffer<bufferT>::contains(const bufferT *data, pluint length) {
        pluint j;
        for(pluint i = 0 ; i < m_count - length ; i++) {
            for(j = 0 ; j < length ; j++) {
                if(m_data[i + j] != data[j]) {
                    break;
                } else if(j == length - 1 && m_data[i + j] == data[j]) {
                    return true;
                }
            }
        }
        return false;
    }

    template<typename bufferT>
    plbool PLBuffer<bufferT>::containsOneOf(const PLArray<bufferT> &unicodeData) {
        pluint j;
        for(pluint i = 0 ; i < m_count ; i++) {
            for(j = 0 ; j < unicodeData.size() ; j++) {
                if(m_data[i] == unicodeData[j]) {
                    return true;
                }
            }
        }
        return false;
    }

    template<typename bufferT>
    plbool PLBuffer<bufferT>::contains(const PLBuffer<bufferT> &data) {
        return contains(data.data(), data.size());
    }

    template<typename bufferT>
    plbool PLBuffer<bufferT>::contains(const PLArray<bufferT> &data) {
        return contains(data.data(), data.size());
    }

    template<typename bufferT>
    bufferT &PLBuffer<bufferT>::operator[](pluint pos) {
        return m_data[pos];
    }

    template<typename bufferT>
    const bufferT &PLBuffer<bufferT>::operator[](pluint pos) const {
        return m_data[pos];
    }

    template<typename bufferT>
    bool PLBuffer<bufferT>::operator == (PLBuffer<bufferT> data) {
        return compareTo(data) == PLCompareResultSame;
    }

    template<typename bufferT>
    bool PLBuffer<bufferT>::operator != (PLBuffer<bufferT> data) {
        return compareTo(data) != PLCompareResultSame;
    }

    template<typename bufferT>
    bool PLBuffer<bufferT>::operator < (PLBuffer<bufferT> data) {
        return compareTo(data) == PLCompareResultSmaller;
    }

    template<typename bufferT>
    bool PLBuffer<bufferT>::operator > (PLBuffer<bufferT> data) {
        return compareTo(data) == PLCompareResultBigger;
    }

    template<typename bufferT>
    PLBuffer<bufferT> &PLBuffer<bufferT>::operator =(const PLBuffer<bufferT> &b) {
        m_data.resetSizeOfArray(b.count());
        if(b.count() > 0) {
            for(pluint i = 0 ; i < b.m_count ; i++) {
                m_data[i] = b.dataAt(i);
            }
        }
        m_count = b.count();
        return *this;
    }

}

#endif
